#ifndef cope_cc_
#define cope_cc_

#include "cope.h"
#include <iostream>
#include <iomanip>

#define G 0.8

/* ======================================================================
   COPEHeaderClass
   ===============

   ====================================================================== */
int hdr_cope::offset_;

static class CopeHeaderClass : public PacketHeaderClass {
public:
    CopeHeaderClass() : PacketHeaderClass("PacketHeader/Cope",
					  sizeof(hdr_cope)) {
	bind_offset(&hdr_cope::offset_);
    }
} class_hdr_cope;

/* ======================================================================
   COPEClass
   =========

   ====================================================================== */
static class COPEClass : public TclClass 
{
public:
    COPEClass() : TclClass("COPE") {

    }
    TclObject* create(int, const char*const*) {
        return (new COPE);        
    }
    
    
} class_cope;


/* ======================================================================
   COPEHandler
   ===========
                   
   ====================================================================== */
void COPEHandler::handle(Event*) {
  
    cope_inst_->cope_resume();
}       
// Duplicate acks and more than 8 acks
void AckPendHandler::handle(Event* e) {
    // create ctrl pkt and send out
    cope_inst_->ctrl_pkt_handler(e->handler_);
}

// Problem!! Where to call this function to start gc!!!!???
void PacketPoolHandler::start() {
    Scheduler& s = Scheduler::instance();
    first_ = true;

    s.schedule(&cope_inst_->h_pkt_pool_,
	       &cope_inst_->pool_event_,
	       cope_inst_->gc_interval_);
}

/*
 * To handle the garbage collection every gc_interval time.
 * However, for the first period, just reset the b_expired flag,
 * that means keep the previous period packets one more period.
 */
void PacketPoolHandler::handle(Event* e) {
    Scheduler& s = Scheduler::instance();

    //    if(cope_inst_->node_->nodeid() == 1) {
	// cout << cope_inst_->node_->nodeid()
	//      << " -------------------before---------------" << endl;
	// cope_inst_->pkt_pool_->showPool();
	//    }
    
    if(first_) {
	cope_inst_->pkt_pool_->b_expired_ = !cope_inst_->pkt_pool_->b_expired_;
	first_ = false;
    }
    else {
	// cout << "garbage collection" << endl;
	cope_inst_->pkt_pool_->garbage_collection();	
    }

    s.schedule(&cope_inst_->h_pkt_pool_,
	       &cope_inst_->pool_event_,
	       cope_inst_->gc_interval_);

    //    if(cope_inst_->node_->nodeid() == 1) {
	// cout << cope_inst_->node_->nodeid()
	//      << " -------------------after-----------------" << endl;
	// cope_inst_->pkt_pool_->showPool();
	//    }
}

/* ======================================================================
   COPE
   ====
				  
   ====================================================================== */
// these two parameters should be put into the command() function???
COPE::COPE() : ctrl_pkt_timer_(this),
	       h_cope_(this),
	       h_ack_pend_(this), 
	       h_pkt_pool_(this) {
    non_ack_timers_ = new list<NonAckTimer*>;
    cope_prq_ = NULL;
    cope_index_ = 0;
    bind("gc_interval_", &gc_interval_);
    bind("txtime_factor_", &txtime_factor_);    
}

COPE::~COPE() {
    COPE_ND_CLEAR(COPELocalCounter, &local_counter_list_);
    list<NonAckTimer*>::iterator it = non_ack_timers_->begin();
    for( ; it != non_ack_timers_->end(); ) {
	delete (*it);
	it = non_ack_timers_->erase(it);
    }
    delete non_ack_timers_;

    delete cope_prq_;
    delete prob_guess_;
    delete non_acks_;
    delete pend_acks_;
    delete pkt_pool_;
    delete pkt_info_;
    qout.close();
    eout.close();
}
double COPE::txtime(Packet * p) {
    //    double t = mac_->txtime(HDR_CMN(p)->size(), mac_->dataRate_);
    //cout << "execute time is " << t << endl;
    //double t = mac_->txtime(1500, mac_->dataRate_);
    // cout << "++++++++++++++txtime++++++++++ " << HDR_CMN(p)->size()
    // 	 << " " << t << " " << txtime_factor_ * t << endl;
    //return txtime_factor_ * t;
    return txtime_factor_ * 0.01; //???should change back
}

int COPE::command(int argc, const char*const* argv) {
    
    if(argc == 3) {
	// In tcl, add-node should be called prior to cope-init
	if (strcmp(argv[1],"add-node") == 0) {
	    assert(argv[2] == NULL);
	    node_ = (MobileNode *)TclObject::lookup(argv[2]);
	    return (TCL_OK);
	} else if (strcmp(argv[1], "add-mac") == 0) {
	    mac_ = (Mac802_11 *)TclObject::lookup(argv[2]);
	    assert(mac_);
	    mac_->installCopeTap(this);
	    return (TCL_OK);
	}
	else if (strcmp(argv[1], "cope-init") == 0) {
	    // init cope_prq
	    cope_prq_ = new COPEPriQueue();
	
	    //init prob_guess_
	    prob_guess_ = new ProbGuess(node_);
                
	    //init ack queues
	    non_acks_ = new NonAckQueue(); 
	    pend_acks_ = new AckPendQueue(); 
                
	    //init packet pool
	    pkt_pool_ = new PacketPool();

	    //init packet info
	    pkt_info_ = new PacketInfo();
	    
	    LIST_INIT(&local_counter_list_);

	    // timers start
	    h_pkt_pool_.start();
	    ctrl_pkt_timer_.start(CTRL_PKT_PEROID);
	    cope_prq_->setCOPEHandler(&h_cope_);

	    char temp[10];
	    sprintf(temp, "%d", node_->nodeid());
	    string s(temp);
	    string sqpre("cope-tcpap-queue");
	    string sepre("cope-tcpap-encode");
	    string qname = sqpre + s;
	    qout.open(qname.c_str());
	    string ename = sepre + s;
	    eout.open(ename.c_str());
	    
	}
    }
    // call cope_prq_'s command to set targets, and judge dsr or not 
    if(cope_prq_ != NULL) {
	return cope_prq_->command(argc, argv);
    }

}

void COPE::getNeighborList( nb_head * neighbor_list) {
    int local_nd_id = node_->nodeid();
    for (int i = 0; i < God::instance()->nodes(); i++) {
	int nb_id = (God::instance()->getNodes())[i]->nodeid();
	if((local_nd_id != nb_id) 
	   && 
	   ( God::instance()->IsNeighbor(local_nd_id, nb_id) == true))
	  COPE_ND_INSERT(COPENeighbor, neighbor_list, nb_id, 0);
    }
}

void COPE::initNeighborCounters( local_counter_head * counter_list) {
    int local_nd_id = node_->nodeid();
    for (int i = 0; i < God::instance()->nodes(); i++) {
	int nb_id = (God::instance()->getNodes())[i]->nodeid();
	if((local_nd_id != nb_id) 
	   && 
	   ( God::instance()->IsNeighbor(local_nd_id, nb_id) == true))
	  COPE_ND_INSERT(COPELocalCounter, counter_list, nb_id, 0);
    }
}

/*
 * Overhear packets from neighbors, only sendup packets not sent to me
 */
void COPE::tap(const Packet* p) {
    int prev_hop = ETHER_ADDR(HDR_MAC802_11(p)->dh_ta);
    if(prev_hop == node_->address()
       || HDR_CMN(p)->next_hop() == node_->address()
       || HDR_CMN(p)->next_hop() < 0)
	return;
    //    cout << "+++++++++++++++++tap++++++++++++++++++++" << endl;
    recv(p->copy(), (Handler*)0);
}

/*
 * called by upper or lower layer
 */
void COPE::recv(Packet * p, Handler * h) {
    struct hdr_cmn *ch = HDR_CMN(p);
    // if(HDR_CMN(p)->ptype() == PT_TCP && HDR_TCP(p)->seqno() == 401) {
    // 	cout << node_->nodeid() << endl;
    // }
    if(cope_prq_->isEmpty())
	ctrl_pkt_timer_.start(CTRL_PKT_PEROID);
    
    if(ch->direction() == hdr_cmn::DOWN) { //DOWN

	/* If I am the source node, set cope pkt header
	   and put p into packet pool */
	if(HDR_CMN(p)->ptype() == PT_CBR
	   || HDR_CMN(p)->ptype() == PT_TCP
	   || HDR_CMN(p)->ptype() == PT_ACK
	   || HDR_COPE(p)->ctype_ == COPE_CTRL) {
	    // if(node_->nodeid() == 0)
	    // 	cout << "send " << HDR_CMN(p)->uid_ << endl;
	   
	    cope_prq_->cope_prq_senddown(p, this);   
	    return;
	} else {
	    if(cope_prq_->dsr_)
		cope_prq_->CMUPriQueue::recv(p, h);
	    else
		cope_prq_->PriQueue::recv(p, h);	
	}
    } else {                               //UP
        
	Packet* cp = p->copy();
	Packet** recv_pkt = &cp;
	int prev_hop = ETHER_ADDR(HDR_MAC802_11(p)->dh_ta);
	struct hdr_cmn* ch = HDR_CMN(p);
	nsaddr_t next_hop = ch->next_hop();

	if(HDR_CMN(p)->ptype() != PT_CBR
	   && HDR_CMN(p)->ptype() != PT_TCP
	   && HDR_CMN(p)->ptype() != PT_ACK
	   && HDR_COPE(p)->ctype_ != COPE_CTRL)
	{
	    if(next_hop == node_->nodeid()
	       || next_hop < 0) {
		(cope_prq_->up_target_)->recv(cp,(Handler *) 0);		
	    }
	    return;
	}
	// if(node_->nodeid() == 5) {
	//     if( HDR_CMN(p)->cope_pkt_id() == -68595)
	// 	cope_prq_->showQueue(this);
	// }
	// switch(HDR_CMN(p)->ptype()) {
	// case PT_TCP:
	//     cout << CUR_TIME << " TCP: node " << node_->nodeid() << " cope recv up packet " 
	// 	 << HDR_CMN(p)->cope_pkt_id()
	// 	 << " " << HDR_CMN(p)->uid_
	// 	 << " " << HDR_TCP(p)->seqno_ 
	// 	 << " from " <<prev_hop << endl;
	//     break;
	// case PT_ACK:
	//     cout << CUR_TIME << " ACK: node " << node_->nodeid() << " cope recv up packet " 
	// 	 << HDR_CMN(p)->cope_pkt_id()
	// 	 << " " << HDR_CMN(p)->uid_
	// 	 << " " << HDR_TCP(p)->seqno_ 
	// 	 << " " << HDR_TCP(p)->ackno() 
	// 	 << " from " << prev_hop << endl;
	//     break;
	// case PT_CBR:
	//     cout <<  CUR_TIME << " CBR: node " << node_->nodeid() << " cope recv up packet " 
	// 	 << HDR_CMN(p)->cope_pkt_id()
	// 	 << " from " << prev_hop << endl;
	// default:
	//     cout <<  CUR_TIME << " Ctrl: node " << node_->nodeid() << " cope recv up packet " 
	// 	 << HDR_CMN(p)->cope_pkt_id()
	// 	 << " from " << prev_hop << endl;
	// }		    
	    //	}

	//update the packet info for previous hop
	pkt_info_->update_pkt_info(p, prev_hop); 

	/* native or decodable packet which is recievable */
	int recv_tag = receivable(recv_pkt);
   
	/* put a cope of  recv_pkt into packet pool, it could be:
	 * 1. an decoded native packet(decodable == 1)
	 * 2. a normal native packet (native == 1) 
	 * 3. an encoded cope packet
	 */

	/* also update the packet info for prev_hop */
	if (recv_tag == 1 || recv_tag == 2) { // native
	  
	  pkt_pool_->in_pool(p->copy());
	  if(recv_tag == 1) // native and receivable
	  {
	      if( HDR_COPE(*recv_pkt)->ctype_ == COPE_RETX) {
		  int prev = ETHER_ADDR(HDR_MAC802_11(*recv_pkt)->dh_ta);
		  u_int16_t loc = HDR_COPE(*recv_pkt)->xoreds_[0].local_seq_num_;
		  if(loc > 0) {
		      pend_acks_->enqueue_pend(prev, loc, false, this);
		      HDR_COPE(*recv_pkt)->xoreds_[0].local_seq_num_ = 0;
		  }
	      }
	      Packet::free(*recv_pkt);
	      HDR_COPE(p)->ctype_ = COPE_DATA;
	      (cope_prq_->up_target_)->recv(p->copy(),(Handler *) 0);
	  }
	 
	} else if (recv_tag == 3){
	    HDR_CMN(*recv_pkt)->direction() = hdr_cmn::UP;
	    pkt_pool_->in_pool((*recv_pkt)->copy());
	    // cout << CUR_TIME << " COPE: " << node_->nodeid() << " decode out a packet " 
	    // 	 << HDR_CMN(*recv_pkt)->uid() << " " << HDR_CMN(*recv_pkt)->cope_pkt_id() << endl;
	    
	    /* 
	     * send pkt to upper layer target, actually the handler
	     * is unuseful for the uptarget_, and it's not reasonable
	     * to pass h_cope_ as a handler 
	     */
	    (cope_prq_->up_target_)->recv(*recv_pkt,(Handler *) 0);
	} else { // recv_tag == zero
	    if(HDR_COPE(p)->ctype_ != COPE_CTRL)
		pkt_pool_->in_pool(p->copy()); //an encoded packet, will be drop   
	}
	
	Packet::free(p);
    }
}

void COPE::cope_resume(){
    cope_prq_->cope_prq_resume(this);
}

/**
 * return whether a encoded packet(true) or native packet(flase)
 */
int COPE::encode(Packet * p, Packet ** res_pkt, Packet** max_size_pkt) {
    nb_head remain_ndlist;
    LIST_INIT(&remain_ndlist);
    nb_pkt_head cope_next_hops;
    LIST_INIT(&cope_next_hops); 
    bool pkt_size_tag = true; //large size packet by default

    //    if(node_->nodeid() == 1) {
	// cout << setw(10) << "encode "
	//      << setw(10) << Scheduler::instance().clock()
	//      << setw(8) << HDR_CMN(p)->uid_
	//      << setw(8) << HDR_TCP(p)->seqno()
	//      << setw(8) << HDR_CMN(p)->cope_pkt_id()
	//      << setw(8) << HDR_CMN(p)->next_hop();
	// if(HDR_CMN(p)->ptype() == PT_ACK)
	//     //		    cope_inst->qout
	//     cout << setw(8) << "ack" << endl;
	// else if(HDR_COPE(p)->ctype_ == COPE_RETX)
	//     //		    cope_inst->qout
	//     cout << setw(8) << "crx" << endl;
	// else
	//     //		    cope_inst->qout << endl;
	//     cout << endl;

	//	cope_prq_->showQueue(this);
	//    }
    
    //get the neighbor list
    getNeighborList(&remain_ndlist);
    
    // make a copy of p firstly, the passed res_pkt = p
    //res_pkt = p->copy();

    //get the next hop of packet res_pkt
    struct hdr_cmn* ch = HDR_CMN(*res_pkt);
    nsaddr_t next_hop_addr = ch->next_hop();
    int next_hop_id_p = (Node::get_node_by_address(next_hop_addr))->nodeid();   
 
    // insert the next hop into NEXTHOPS
    // note that original p is inserted rather than res_pkt
    COPE_ND_INSERT(COPENeighborPkt, &cope_next_hops, next_hop_id_p, p);
  
    //delete the next hop from cope's remained node list
    COPE_ND_DELETE(COPENeighbor, remain_ndlist, next_hop_id_p);

    //check the packet is small or large
    if(ch->size() < PACKET_SIZE_THRESHOLD) {
	pkt_size_tag = false; //search from small virtual queue first
    }

    // cope_prq_->showQueue(this);
    // cope_prq_->cope_vir_table_->show();
 
    //iterate nodeid from remained node list 
    if(pkt_size_tag == true) { 
	//for large virqueue first
	traverse_remained_nbs('L', &cope_next_hops, &remain_ndlist);
	traverse_remained_nbs('S', &cope_next_hops, &remain_ndlist);
    } else {
	//for small virqueue first
	traverse_remained_nbs('S', &cope_next_hops, &remain_ndlist);
	traverse_remained_nbs('L', &cope_next_hops, &remain_ndlist);
    }

    // cope_prq_->showQueue(this);
    // cope_prq_->cope_vir_table_->show();
    
    //xor the packet to encoded packet data and 
    //put this pkt infomation into cope header

    // assemble HDR_COPE
    COPENeighborPkt* cope_iter; 
    COPE_GET_HEAD(&cope_next_hops, cope_iter); 
    COPE_GET_NEXT(cope_iter, cope_iter); 
  
    //xor res_pkt(copy of p) into cope header
    
    if( cope_iter != NULL ) {
      //Packet* cpkt[3];
        Packet** pcpkt[3];
	int index = 0;
	int max_size = HDR_CMN(*res_pkt)->size();
	COPE_GET_HEAD(&cope_next_hops, cope_iter); 
	// cout << CUR_TIME << " COPE: " << node_->nodeid()
	//      << " " << HDR_CMN(p)->uid_
	//      << " " << HDR_CMN(p)->cope_pkt_id()
	//      << " " << HDR_TCP(p)->seqno()
	//      << " Encode packets " << endl;
	COPELocalCounter* local = NULL;
	//	HDR_COPE(res_pkt)->hdr_xored_list_ = new list<hdr_xored_t*>;
        //cout << "encode packts:" << endl;
	eout << endl;
	do {
	    /* add xor headers */
	    hdr_xored_t hdr_xored_pkt;
	    hdr_xored_pkt.pkt_id_ = cope_iter->get_pkt_id();
	    hdr_xored_pkt.nexthop_ = cope_iter->getid();
	    
	    if(HDR_COPE(*res_pkt)->ctype_ == COPE_RETX) {
		hdr_xored_pkt.local_seq_num_ =
		    non_acks_->get_local_seq(hdr_xored_pkt.nexthop_,
					     hdr_xored_pkt.pkt_id_);
		if(hdr_xored_pkt.local_seq_num_ == 0)
		    return 0;
	    } else {
		/* increase the local sequence number for each next hop */
		// lookup the local seq-num counter for a specific node
		COPE_ND_LOOKUP(COPELocalCounter, 
			       local_counter_list_, 
			       cope_iter->getid(), 
			       local);
		
		if(!local) { // this is a new neighbor
		    // firstly, insert this new neighbor
		    COPE_ND_INSERT(COPELocalCounter,
				   &local_counter_list_,
				   cope_iter->getid(),
				   0);
		    // then, get this neighbor's COPELocalCounter*
		    COPE_ND_LOOKUP(COPELocalCounter, 
				   local_counter_list_,
				   cope_iter->getid(), 
				   local);
		}

		local->local_seq_counter_ ++; // if local_seq_counter_ > 2^16 - 1 ?
	    	HDR_COPE(*res_pkt)->ctype_ = COPE_DATA;
		hdr_xored_pkt.local_seq_num_ = local->local_seq_counter_;
	    }

	    if(HDR_CMN(cope_iter->get_pkt())->size() > max_size) {
		max_size = HDR_CMN(cope_iter->get_pkt())->size();
		*max_size_pkt = cope_iter->get_pkt();
	    }
	    hdr_xored_pkt.retx_counter_ =
		non_acks_->enqueue(hdr_xored_pkt.nexthop_,
				   hdr_xored_pkt.local_seq_num_,
				   cope_iter->get_pkt()->copy());

	    pcpkt[index] = (Packet**)malloc(sizeof(Packet *));
	    non_acks_->lookup_packet(hdr_xored_pkt.nexthop_,
				     hdr_xored_pkt.local_seq_num_, pcpkt[index]);

	    eout << setw(10) << Scheduler::instance().clock()
	    	<< setw(8) << node_->nodeid()
	    	<< setw(8) << hdr_xored_pkt.pkt_id_
	    	<< setw(8) << HDR_TCP(cope_iter->get_pkt())->seqno()
	    	<< setw(8) << hdr_xored_pkt.local_seq_num_
	    	<< setw(8) << hdr_xored_pkt.nexthop_;
	    if(HDR_CMN(cope_iter->get_pkt())->ptype() == PT_ACK)
	    	eout << setw(8) << "ack" << endl;
	    else if(HDR_COPE(cope_iter->get_pkt())->ctype_ == COPE_RETX)
	    	eout << setw(8) << "crx" << endl;
	    else
	    	eout << endl;

	    append_xored_pkt(cope_iter->get_pkt(), hdr_xored_pkt, *res_pkt, pcpkt[index]);
            //cout << "encode addr" << pcpkt[index] <<" "<< *pcpkt[index]<<endl;
	    // put all xored packets into the non-ack queue
	    index++;

	    COPE_GET_NEXT(cope_iter, cope_iter);
	} while (cope_iter != NULL );     
	// cout << endl;
	
	return 1;//represent a encoded packet
    } else {
        //can send the native packet directly, free the copy and return p
        return 0; //represent a native packet
    }
}

/**
 * called by traverse_remained_nbs, return encodable or not 
 */
int COPE::encodable(char type , 
                    int nb_iter_id ,
                    nb_pkt_head * encode_next_hops ) {
    // denote the probability of node(nb_iter_id) having an 
    // encoded packet which should be already in encode_next_hops
    double prob = 1;
    
    // opposite with prob, denote the probability of an already encoded packet
    // having the checked encoding candidate
    
    
    // get a candidate
    VirQueue * vir_table = cope_prq_->cope_vir_table_;
    list<Packet*> * pkt_q = vir_table->get_vir_pktq(nb_iter_id, type);
 
    if(pkt_q == NULL)
	return 0;
    Packet * pkt_candidate = pkt_q->front();
    // if(pkt_candidate) {
    // 	cout << HDR_CMN(pkt_candidate)->uid_
    // 	     << " " << HDR_CMN(pkt_candidate)->cope_pkt_id();
    // 	if(HDR_COPE(pkt_candidate)->ctype_ == COPE_CTRL)
    // 	    cout << " ctrl";
    // 	else if(HDR_CMN(pkt_candidate)->ptype() == PT_TCP)
    // 	    cout << " tcp" << endl;
    // 	else if(HDR_CMN(pkt_candidate)->ptype() == PT_ACK)
    // 	    cout << " ack" << endl;
    // 	else if(HDR_COPE(pkt_candidate)->ctype_ == COPE_RETX)
    // 	    cout << " crx" << endl;
    // 	else if(HDR_COPE(pkt_candidate)->ctype_ == COPE_DATA)
    // 	    cout << " cope" << endl;
    // 	else
    // 	    cout << endl;
    // }
    
    // iterate each packet in {NextHops} and check the encoded candidate
    // can be encoded together or not, prob will be computed accumulately
    COPENeighborPkt * encoded_pkt_iter;
    COPE_GET_HEAD(encode_next_hops, encoded_pkt_iter);
	    
    for (; encoded_pkt_iter != NULL;) {
   
	if((pkt_info_->get_prob(nb_iter_id, encoded_pkt_iter->get_pkt_id()))  
	   == true) {
	    // neighbor nb_iter_id has this specific encoded pkt
	    // keep prob unchaged, which means prob = prob * 1
	    prob = prob * 1.0;

	} else {
	    // check the probability of nb_iter_id has this encoded pkt 
	    int pkt_src_ip =  HDR_IP(encoded_pkt_iter->get_pkt())->saddr();
	  
	    prob = prob * prob_guess_->get_prob_guess(pkt_src_ip,
						      nb_iter_id);	    
	} 

	// 1. Check the nb_iter_id has all already encoded packet,
	//    so that it can decode ------------if
	// 2. Check the nb_pkt_iter->get_id() (the nexthop of an 
	//    already encoded item) can decode new_encoded_pkt or 
	//    not-------------------------------else

	if(prob <= G ) {
	    break; // no need to other have encoded packets

	} else {	  
	    double old_prob = encoded_pkt_iter->get_encode_prob();
	    double cur_prob, new_prob;
	    if(pkt_info_->get_prob(encoded_pkt_iter->getid(),
				    HDR_CMN(pkt_candidate)->cope_pkt_id())) {
		encoded_pkt_iter->update_encode_prob( old_prob * 1.0 );
	    } else {
		// check the probability of nb_iter_id has pkt 
		int pkt_src_ip = HDR_IP(pkt_candidate)->saddr();
		cur_prob = 
		    prob_guess_->get_prob_guess(pkt_src_ip,
						encoded_pkt_iter->getid());	
		new_prob = cur_prob * old_prob;
		if( new_prob <= G )
		    prob = 0;
	    } 
	}

	COPE_GET_NEXT(encoded_pkt_iter,encoded_pkt_iter);
    }
          
    if(prob > G) {
	//put this packet into cope_next_hops, 
	//whoes pkts will be encoded later
	COPE_ND_INSERT(COPENeighborPkt, 
		       encode_next_hops,
		       nb_iter_id, 
		       pkt_candidate);
	
	// update the encode_prob for each element in {NextHops}/pkt_candidate
	COPE_GET_HEAD(encode_next_hops, encoded_pkt_iter);
	int pkt_src_ip = HDR_IP(pkt_candidate)->saddr();
	while(encoded_pkt_iter != NULL) {
	  if(encoded_pkt_iter->getid() != nb_iter_id) {
	    encoded_pkt_iter->
	      update_encode_prob(encoded_pkt_iter->get_encode_prob()
				 * 
				 prob_guess_->get_prob_guess(pkt_src_ip, 
							     encoded_pkt_iter->
							     getid()));
	  } else {
	    encoded_pkt_iter->update_encode_prob(prob);
	  }
	  COPE_GET_NEXT(encoded_pkt_iter,encoded_pkt_iter);
	}	
	return 1; //encodable
    }
    return 0; // not encodable
}

/**
 * called by encode, traverse all {Neighbors} - {NextHops} pkts 
 */
void COPE::traverse_remained_nbs(char type,
                                 nb_pkt_head * encode_next_hops,
				 nb_head * remain_nb_list ) {
    COPENeighbor * nb_iter;
    int nb_iter_id;
    // a packet is encodable or not, undecodable by default
    bool encodable_tag = 0;  

    COPE_GET_HEAD(remain_nb_list, nb_iter);

    while ( nb_iter != NULL ) {
	nb_iter_id = nb_iter->getid();
	encodable_tag = encodable(type, nb_iter_id, encode_next_hops );

	COPE_GET_NEXT(nb_iter, nb_iter);

	if(encodable_tag == 1) {      
	    // delete this neighbor from left list, because it is already
	    // regarded as a encoded next hop
	    COPE_ND_DELETE(COPENeighbor, *remain_nb_list, nb_iter_id);
	} 
    }
}

/*
 * During the append xored header process, simulate xor all encode packet
 * together and store the encoded result to res_pkt, also delete the packets
 * from output queue and virtual queue
 */
void COPE::append_xored_pkt(Packet * del_pkt,
			    hdr_xored_t xored, 
			    Packet * res_pkt,
			    Packet** xor_pkt_addr) {
    /* xor all pkts together and store it to res_pkt, just simulate this!*/
    // XOR......
    u_int8_t index = HDR_COPE(res_pkt)->encoded_num_;
    /* Assemble hdr_xored_pkt into pkt's cope header */
    HDR_COPE(res_pkt)->xoreds_[index].pkt_id_ = xored.pkt_id_;
    HDR_COPE(res_pkt)->xoreds_[index].nexthop_ = xored.nexthop_;
    HDR_COPE(res_pkt)->xoreds_[index].local_seq_num_ = xored.local_seq_num_;
    HDR_COPE(res_pkt)->xoreds_[index].retx_counter_ = xored.retx_counter_;
    HDR_COPE(res_pkt)->xoreds_[index].ppkt_addr_ = xor_pkt_addr;
    HDR_COPE(res_pkt)->encoded_num_ ++;
    
    // cout<< " " << xored.pkt_id_ << " size: " << HDR_CMN(*xor_pkt_addr)->size()
    // 	<< " seq: " << HDR_TCP(*xor_pkt_addr)->seqno_
    // 	<< " loc: " << xored.local_seq_num_;
    /*
     * dequeue encoded packet from output queue, xor all packets together,
     * just simulate this encoding operation, this operation will also 
     * delete the corresponding pkt in virtual queue
     */
    if(cope_prq_->cope_dequeue_item(del_pkt))
	cope_prq_->cope_vir_table_->remove_vir_pkt(del_pkt);
    
    Packet::free(del_pkt);
}

void COPE::append_state_report(Packet * p) { 
    Node * tnode = (node_->nodehead_).lh_first;
    hdr_report_t* rpt;

    for (; tnode; tnode = tnode->nextnode() ) {
        /*
	 * get_report() should return false if nodeid doesn't exist
	 */
	if(tnode->nodeid() == node_->address())
	    continue;
	rpt = pkt_pool_->get_report(tnode->nodeid());
	if(rpt != NULL) {
	    u_int8_t index = HDR_COPE(p)->report_num_;
	    HDR_COPE(p)->reports_[index].src_ip_ = rpt->src_ip_;
	    HDR_COPE(p)->reports_[index].last_pkt_ = rpt->last_pkt_;
	    HDR_COPE(p)->reports_[index].bit_map_ = rpt->bit_map_;
	    HDR_COPE(p)->report_num_ ++;

	    free(rpt);
	}
    }
}

void COPE::append_acks(Packet * p) {
    hdr_ack_t* ack;
    nb_head neighbor_list;
    LIST_INIT(&neighbor_list);
    getNeighborList(&neighbor_list);

    COPENeighbor * nb_iter; 
    COPE_GET_HEAD(&neighbor_list, nb_iter); 

    //    cout << "node: " << node_->nodeid() << "----append-acks---"  << endl;
    
    while(nb_iter != NULL){
	ack = pend_acks_->get_pend(nb_iter->getid());	
	if(ack != NULL) {
	    //Assemble hdr_pend into pkt's cope header
	    u_int8_t index = HDR_COPE(p)->ack_num_;
	    HDR_COPE(p)->acks_[index].neighbor_ = ack->neighbor_;
	    HDR_COPE(p)->acks_[index].last_ack_ = ack->last_ack_;
	    HDR_COPE(p)->acks_[index].ack_map_ = ack->ack_map_;
	    HDR_COPE(p)->ack_num() = HDR_COPE(p)->ack_num() + 1;

	    free(ack);
	}
	COPE_GET_NEXT(nb_iter, nb_iter);
    }
    COPE_ND_CLEAR(COPENeighbor, &neighbor_list);
}


int COPE::receivable( Packet** recv_pkt) {
    // can decode by default
    int decodable = 1;

    // native packet by default
    int native = 1;

    /* 
     * update the packet info according to state field, 
     * and delete these state reports after updating
     */ 
    extract_state_report(*recv_pkt);

    /* 
     * update the non ack queue according to ack field, 
     * and delete these acks after updating
     */ 
    extract_acks(*recv_pkt);

    /* If it is a control packet, just send it to CtrlAgent and drop it */
    if(HDR_COPE(*recv_pkt)->ctype_ == COPE_CTRL) {
        return 0; // as a non-receivable packet
    }

    // Decode only if it is an encoded packet
    if(HDR_COPE(*recv_pkt)->encoded_num_ > 0) {      
	/* 
	 * the recv_pkt will be the decoded result if it is decodable,
	 * but here we just use pointer p instead, since we don't need
	 * to real decode the packet data, just clear all xored header
	 * of p is enough 
	 *
	 * if it is not decodable, the result will be copy of original
	 * encoded p, keeping the xored header alive, in order to put
	 * its copy into packet pool
	 */

        native = 0;
	// the recv_pkt will be freed if return value of decode is zero
	decodable = decode(recv_pkt);
    }

    /* the native packet should be receivable */
    if( native == 1 ) {
        int id = HDR_CMN(*recv_pkt)->next_hop();

	if(node_->nodeid() == id )
	  return 1; // native and receivable, can be sent out
	else
	  return 2; // native but non-receivable
    } else if (decodable == 1) {      
        return 3; //decodable, receivable, and decoded packet can be sent out  
    } else {
	/* 
	 * Non-receivable: include two situation
	 * 1. can not decode  2, decodable but I am not the receiver 
	 * 3. decodable but the decoded packet on longer exist
	 */
      return 0; 
    } 
}


/**
 * return the packet decodable status and the decoded_pkt pointer,
 * 0 - Non-decodable(include the decoded packet has been dropped 
 * at sender because of over retransmission, we regard this situation
 * as a non-decodable case) & (overhear packet) & (can not decode); 
 * 1 - Decodable and return a valid packet;
 * But in a simulation, no real decode operation, so just set
 * a xor-header cleared p as a result is enough
 */
int COPE::decode(Packet** decoded_pkt) {
    int decodable = 1; // decodable by default
    int overhear = 1; // overhear by default
    bool flag = true;
    Packet** decoded_ppkt_addr;
    u_int16_t loc;
    u_int8_t rc;
    int prev =  ETHER_ADDR(HDR_MAC802_11(*decoded_pkt)->dh_ta);
   
    for(int i = 0; i < HDR_COPE(*decoded_pkt)->encoded_num_; i ++) {
	// still can decode and node is not the nexthop, decode this pkt out 
	if(HDR_COPE(*decoded_pkt)->xoreds_[i].nexthop_ != node_->nodeid()) {
	    //check whether the nexthop exists in packet pool	  
	    Packet * encode_pkt =
		pkt_pool_->get_packet(HDR_COPE(*decoded_pkt)->xoreds_[i].pkt_id_);
	    
	    //opps! can not decode
	    if( encode_pkt == NULL ) {
		decodable = 0;
	    }
	} else {
	    //*decoded_local_seq = HDR_COPE(decoded_pkt)->xoreds_[i].local_seq_num_;
	    decoded_ppkt_addr = HDR_COPE(*decoded_pkt)->xoreds_[i].ppkt_addr_;
	    //cout<<decoded_ppkt_addr <<" " << *decoded_ppkt_addr << endl;
	    if( !(*decoded_ppkt_addr)->getFlag()) {
		flag = false;
		
		free(*decoded_ppkt_addr);
		//cout << "free packet" << endl;
		//(*decoded_ppkt_addr) == NULL;
		decodable = 0;
	    } else if(*decoded_ppkt_addr == NULL) {
		flag = false;
		decodable = 0;	
	    }
	    loc = HDR_COPE(*decoded_pkt)->xoreds_[i].local_seq_num_;
	    rc = HDR_COPE(*decoded_pkt)->xoreds_[i].retx_counter_;
	    overhear = 0;
	    if(decodable == 0)
		break;
	}
    }
    //non-decodable include three cases as said above
    if(overhear == 1 || decodable == 0)
    {	
	if(decodable == 0 && overhear==0) {
	    if( rc >= RETRANSMIT_TIMES || flag == false) {		
		pend_acks_->enqueue_pend(prev, loc, true, this);
	    }
	    
	} else {
	    
	    decodable = 0;
	}
	Packet::free(*decoded_pkt);
	return decodable;
    }

    
    // Decodable packet, return a decoded packet whose nexthop is me
    Packet::free(*decoded_pkt);
    *decoded_pkt = (*decoded_ppkt_addr)->copy();
    // cout<< "decoded: " << HDR_CMN(*decoded_pkt)->cope_pkt_id()
    // 	<< " " << HDR_TCP(*decoded_pkt)->seqno() << endl;
    //(*decoded_ppkt_addr)->setFlag(false);
    pend_acks_->enqueue_pend(prev, loc, false, this);

    return decodable;
}

/**
 * update pkt_info for a node according to the state report, 
 * and delete state report field in cope header
 */
void COPE::extract_state_report(Packet * p) {
    // check whether there is state report in this packet
    if(HDR_COPE(p)->report_num_ == 0)
	return;

    int src_node_id = ETHER_ADDR(HDR_MAC802_11(p)->dh_ta);

    for(int i = 0; i < HDR_COPE(p)->report_num_; i ++) {
	pkt_info_->update_pkt_info(HDR_COPE(p)->reports_[i], src_node_id);
    }
    
    // update the prob_guess_ use the updated pkt_info
    nb_head neighbor_list ;
    LIST_INIT(&neighbor_list);
    getNeighborList(&neighbor_list);
    prob_guess_->up_prob_info(src_node_id, 
			      &neighbor_list,
			      cope_prq_->cope_vir_table_,
			      pkt_info_);
    COPE_ND_CLEAR(COPENeighbor, &neighbor_list);

    HDR_COPE(p)->report_num_ = 0;
}

/**
 * if nodeid  == neighbor, update non-ack queue for the node according to the
 * ack information, and then delete all ack information field in cope header
 */
void COPE::extract_acks(Packet * p) {    
    // check whether there is ack block in this packet
    if((int)HDR_COPE(p)->ack_num_ == 0)
	return;
    // get the previous hop to delete it from NonAckQueue if acked
    int prev_hop = ETHER_ADDR(HDR_MAC802_11(p)->dh_ta);
    Packet* pkt = NULL;
 
    for(int i = 0; i < HDR_COPE(p)->ack_num_; i ++) {
	if(HDR_COPE(p)->acks_[i].neighbor_ == node_->nodeid()) {
	    int loc = HDR_COPE(p)->acks_[i].last_ack_ - ACKMAP_SIZE;
	    for(int j = 0; j < ACKMAP_SIZE; j ++) {
	        // cout << bitset<8>(HDR_COPE(p)->acks_[i].ack_map_) << endl;
		if(loc >= 0) {
		    if(HDR_COPE(p)->acks_[i].ack_map_ & 0x80) {	      
			u_int8_t rc = non_acks_->lookup_packet(prev_hop, loc + 1, &pkt);
			if(pkt == NULL) {
			    HDR_COPE(p)->acks_[i].ack_map_ <<= 1;
			    loc ++;
			    continue;
			}
			stop_timer(prev_hop, loc + 1);
			if(rc > 0) {
			    if(cope_prq_->cope_dequeue_item(pkt))
				cope_prq_->cope_vir_table_->remove_vir_pkt(pkt);
			}
			non_acks_->delete_entry(prev_hop, loc + 1,true);
		    } else {
		      retrasmit(prev_hop, loc + 1);
		    }
		}
		HDR_COPE(p)->acks_[i].ack_map_ <<= 1;
		loc ++;
	    }
	}
    }
    HDR_COPE(p)->ack_num_ = 0;
}

/*
 * This function should be called whenever there is a packet sent out.
 */
void COPE::set_pkt_header(Packet* p) {
    struct hdr_cope* hc = HDR_COPE(p);
    hc->encoded_num_ = 0;
    hc->report_num_ = 0;
    hc->ack_num_ = 0;

    int pktid = 0;
    struct hdr_cmn* ch = HDR_CMN(p);

    /*
     * There would be a problem when cope_seq_num_ >= 2^16, so we should
     * reset cope_seq_num_ to 0 when cope_seq_num_ == 2^16 - 1.
     */
    
    if(cope_index_ == ((1 << 16) - 1)) // problems!!!
	cope_index_ = 0;
    cope_index_ ++;
    HDR_CMN(p)->cope_seq_num() = cope_index_;

    /*
     * pkt-id is the hash of source ip address and ip sequence number,
     * HASH_JEN_MIX (hash function in uthash) is used to generate pkt-id.
     */
    nsaddr_t src = HDR_IP(p)->saddr();
    u_int16_t seq = ch->cope_seq_num();
    HASH_JEN_MIX(src, seq, pktid);
    ch->cope_pkt_id() = pktid;
}


void COPE::start_timer(Packet *p, double timeout) {
    u_int8_t rc;
    for(int i = 0; i < HDR_COPE(p)->encoded_num_; i ++) {
    	NonAckTimer* timer = new NonAckTimer(this);
	rc = HDR_COPE(p)->xoreds_[i].retx_counter_;
	if( rc == 0)
	    rc = 1;
	else
	    rc <<= 1;
    	timer->start(timeout * rc,
    		     HDR_COPE(p)->xoreds_[i].nexthop_,
    		     HDR_COPE(p)->xoreds_[i].local_seq_num_);
	
    	non_ack_timers_->push_back(timer);
    }
}

void COPE::stop_timer(Packet* p) {
    Scheduler &s = Scheduler::instance();
    Event* e = s.lookup(p->uid_);
    ((CopeTimer*)(e->handler_))->stop(); // maybe problems!!!
}

void COPE::stop_timer(int nodeid, u_int16_t local) {
    list<NonAckTimer*>::iterator it = non_ack_timers_->begin();

    for( ; it != non_ack_timers_->end(); it ++) {
    	if((*it)->nonack_nodeid_ == nodeid && (*it)->nonack_local_seqnum_ == local) {
    	    (*it)->stop();
    	    delete (*it);
    	    non_ack_timers_->erase(it);
    	    break;
    	}
    }
}


/*
 * Insert the time-out packet at the head of the output queue and retransmit.
 */
void COPE::non_ack_handler(Event* e) {

    NonAckTimer* t = (NonAckTimer*)(e->handler_);

    int nodeid = t->nonack_nodeid_;
    u_int16_t local = t->nonack_local_seqnum_;
    Packet* p = NULL;
    u_int8_t rc = non_acks_->get_packet(nodeid, local, &p);
    
    /*
     * If the retrasmission times bigger than the threshold, stop this timer
     * and delete the packet info in NonAckQueue, then return directly.
     */
    if(p == NULL)
	return;
    
    if(rc > RETRANSMIT_TIMES) {
	stop_timer(nodeid, local);
	// if(cope_prq_->cope_dequeue_item(p))
	//     cope_prq_->cope_vir_table_->remove_vir_pkt(p);
	// cout <<CUR_TIME << " COPE: delete non ack since rc > RTIMES" << endl;
        non_acks_->delete_entry(nodeid, local,false);
	return; 
    }

    if(cope_prq_->cope_dequeue_item(p))
        cope_prq_->cope_vir_table_->remove_vir_pkt(p);

    list<NonAckTimer*>::iterator it = non_ack_timers_->begin();
    for( ; it != non_ack_timers_->end(); it ++) {
    	if((*it)->nonack_nodeid_ == nodeid && (*it)->nonack_local_seqnum_ == local) {
	    // (*it)->stop();
    	    delete (*it);
    	    non_ack_timers_->erase(it);
    	    break;
    	}
    }
    
    Packet * cp = p->copy();
    HDR_COPE(cp)->ctype_ = COPE_RETX;
    u_int16_t loc = non_acks_->get_local_seq(HDR_CMN(cp)->next_hop(), HDR_CMN(cp)->cope_pkt_id());
    // cout << endl << "+++++++++++++timeout+++++++" << endl;
    // cout << setw(10) << Scheduler::instance().clock()
    // 	 << setw(8) << HDR_CMN(cp)->uid_
    // 	 << setw(8) << HDR_TCP(cp)->seqno()
    // 	 << setw(8) << HDR_CMN(cp)->cope_pkt_id()
    // 	 << setw(8) << node_->nodeid() << " -> "
    // 	 << HDR_CMN(cp)->next_hop()
    // 	 << endl;
    
    HDR_COPE(cp)->xoreds_[0].local_seq_num_ = loc;
    // cout <<CUR_TIME << " retransmit since time expire" << rc 
    // 	 << "times, with loc "<< loc <<endl;
    recv(cp, &h_cope_);
}

void COPE::retrasmit(int nodeid, u_int16_t local) {
    Packet* p = NULL;
    u_int8_t rc = non_acks_->lookup_packet(nodeid, local, &p);
    /*
     * If the retrasmission times bigger than the threshold, stop this timer
     * and delete the packet info in NonAckQueue, then return directly.
     */
    if(p == NULL)
	return;
    
    if(rc > RETRANSMIT_TIMES) {
    	stop_timer(nodeid, local);
	// cout <<CUR_TIME << " COPE: delete non ack since rc > RTIMES/ in retransmit" << endl;
    	// if(cope_prq_->cope_dequeue_item(p))
    	//     cope_prq_->cope_vir_table_->remove_vir_pkt(p);
	
        non_acks_->delete_entry(nodeid, local,false);
    	return; 
    }

    if(cope_prq_->cope_dequeue_item(p))
        cope_prq_->cope_vir_table_->remove_vir_pkt(p);
    
    list<NonAckTimer*>::iterator it = non_ack_timers_->begin();
    for( ; it != non_ack_timers_->end(); it ++) {
    	if((*it)->nonack_nodeid_ == nodeid && (*it)->nonack_local_seqnum_ == local) {
	    (*it)->stop();
    	    delete (*it);
    	    non_ack_timers_->erase(it);
    	    break;
    	}
    }
    
    Packet * cp = p->copy();
    HDR_COPE(cp)->ctype_ = COPE_RETX;
    u_int16_t loc = non_acks_->get_local_seq(HDR_CMN(cp)->next_hop(), HDR_CMN(cp)->cope_pkt_id()); 

    HDR_COPE(cp)->xoreds_[0].local_seq_num_ = loc;

    // cout << endl << "---------retx-------" << endl;
    // cout << setw(10) << Scheduler::instance().clock()
    // 	 << setw(8) << HDR_CMN(cp)->uid_
    // 	 << setw(8) << HDR_TCP(cp)->seqno()
    // 	 << setw(8) << HDR_CMN(cp)->cope_pkt_id()
    // 	 << setw(8) << node_->nodeid() << " -> "
    // 	 << HDR_CMN(cp)->next_hop();
    // if(HDR_CMN(cp)->ptype() == PT_ACK)
    // 	cout << setw(8) << "ack" << endl;
    // else if(HDR_COPE(cp)->ctype_ == COPE_RETX)
    // 	cout << setw(8) << "crx" << endl;
    // else
    // 	cout << endl;

    // cout <<CUR_TIME << " COPE: retransmit packet " <<HDR_CMN(cp)->cope_pkt_id() << " "<<HDR_TCP(cp)->seqno()<<" with loc " << loc << " while recv ack 0" << endl;
	
    recv(cp, &h_cope_);
}

/*
 * allocate a control packet and transmit it
 */
void COPE::ctrl_pkt_handler(Handler * h) {
    Packet * cp = Packet::alloc();

    // set Common Header
    hdr_cmn* ch = HDR_CMN(cp);
    //    ch->uid() = 0;
    ch->ptype() = PT_COPE;
    ch->size() = 10;
    ch->iface() = -2;
    ch->error() = 0;
    ch->direction_ = hdr_cmn::DOWN; //DOWN
    ch->next_hop_ =  (int)IP_BROADCAST;
    //ch->prev_hop_ = node_->address();
	
    //set ip header
    hdr_ip* iph = HDR_IP(cp);
    iph->saddr() = node_->address();
    iph->daddr() = (int)IP_BROADCAST;	
    //    ch->size() += IP_HDR_LEN;

    // mac header 
    hdr_mac* dh = HDR_MAC(cp);
    mac_->hdr_dst((char*) dh, MAC_BROADCAST);
    mac_->hdr_src((char*) dh, mac_->addr());
    dh->ftype_ = MF_DATA;	

    // set cope header
    HDR_COPE(cp)->ctype_ = COPE_CTRL;
    HDR_COPE(cp)->encoded_num_ = 0;
    HDR_COPE(cp)->report_num_ = 0;
    HDR_COPE(cp)->ack_num_ = 0;
   
    /* add the state report and acks info */
    append_state_report(cp);
    append_acks(cp);

    if(HDR_COPE(cp)->report_num_ == 0 && HDR_COPE(cp)->ack_num_ == 0) {
	Packet::free(cp);
	return;
    }
	
    // call the recv of this function
    recv(cp, &h_cope_);
}

#endif
